/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.Collections;
using System.Text;
using System.Xml.Serialization;
using Madason.MetaModel.Validation;

namespace Madason.MetaModel
{
	/// <summary>
	/// Namespace
	/// </summary>
	[Serializable]
	public class Namespace : ModelElement
	{
		#region private members
		private ClassList classes;
		private NamespaceList namespaces;
		private Namespace parent;
		private DependencyList dependencies;
		private StateMachineList statemachines = new StateMachineList();
		#endregion

		#region events

		void namespaces_AfterNamespacesAdded(Namespace ns)
		{
			ns.Parent = this;
		}


		void classes_AfterClassAdded(Class cls)
		{
			cls.Namespace = this;
		}

		#endregion

		




		/// <summary>
		/// fullname of namespace
		/// </summary>
		[XmlIgnore]
		public string FullName
		{
			get
			{
				if (Name == null)
					return "NoNamespace";
				if (Parent == null || Parent is Model) return Name;
				else
					return Parent.FullName + "." + Name;
			}
		}


		/// <summary>
		/// sub namespace
		/// </summary>
		///[XmlArrayItem(typeof(Namespace), ElementName="package", )]
		[XmlElement(ElementName="package", Type=typeof(Namespace))]
		public NamespaceList Namespaces
		{
			get 
			{
				if (namespaces == null)
				{
					namespaces = new NamespaceList();
					namespaces.AfterNamespacesAdded += new NamespaceAddDelegate(namespaces_AfterNamespacesAdded);
				}
				return namespaces; 
			}
			set 
			{ 
				namespaces = value;
				namespaces.AfterNamespacesAdded += new NamespaceAddDelegate(namespaces_AfterNamespacesAdded);
			}
		}


		/// <summary>
		/// Classes in Namespace
		/// </summary>
		[XmlElement(typeof(Class), ElementName = "class")]
		public ClassList Classes
		{
			get 
			{
				if (classes == null)
				{
					classes = new ClassList();
					classes.AfterClassAdded += new ClassAddDelegate(classes_AfterClassAdded);
				}
				return classes; 
			}
			set 
			{ 
				classes = value;
				classes.AfterClassAdded += new ClassAddDelegate(classes_AfterClassAdded);
			}
		}

		/// <summary>
		/// List of contained Statemachines (Activity charts are handled as statemachines also)
		/// </summary>
		public StateMachineList Statemachines
		{
			get
			{
				return statemachines;
			}
			set
			{
				statemachines = value;
			}
		}

		/// <summary>
		/// Obernamespace
		/// </summary>
		[XmlIgnore]
		public Namespace Parent
		{
			get { return parent; }
			set { parent = value; }
		}


		/// <summary>
		/// Access to the Model
		/// </summary>
		[XmlIgnore]
		public Model Model
		{
			get
			{
				if (this is Model) return this as Model;
				else return Parent.Model;
			}
		}

		/// <summary>
		/// search a namespace by id
		/// </summary>
		/// <param name="id"></param>
		public Namespace FindNamespaceById(string id)
		{
			if (Id == id) return this;
			foreach (Namespace ns in Namespaces)
			{
				Namespace result = ns.FindNamespaceById(id);
				if (result != null) return result;
			}
			return null;
		}

		/// <summary>
		/// search a class by id
		/// </summary>
		/// <param name="id"></param>
		public Class FindClassById(string id)
		{
			foreach (Class cls in Classes)
			{
				if (cls.Id == id) return cls;
			}

			foreach (Namespace ns in Namespaces)
			{
				Class result = ns.FindClassById(id);
				if (result != null) return result;
			}
			return null;
		}

		/// <summary>
		/// search a class by name
		/// </summary>
		/// <param name="id"></param>
		public Class FindClassByName(string name)
		{
			if (name == null) return null;
			int posPoint = name.IndexOf(".");
			if (posPoint != -1)
			{
				string nsName = name.Substring(0, posPoint);
				foreach (Namespace ns in Namespaces)
					if (ns.Name == nsName)
						return ns.FindClassByName(name.Substring(posPoint+1));
				return null;
			}

			foreach (Class cls in Classes)
			{
				if (cls.Name == name) return cls;
			}

			foreach (Namespace ns in Namespaces)
			{
				Class result = ns.FindClassByName(Id);
				if (result != null) return result;
			}
			return null;
		}

		/// <summary>
		/// Find a Namespace by its Name in form of Parentnamespace.Childnamespace
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public Namespace FindByName(string name)
		{
			if (name == null) return null;
			int posPoint = name.IndexOf(".");
			if (posPoint != -1)
			{
				string nsName = name.Substring(0, posPoint);
				foreach (Namespace ns in Namespaces)
					if (ns.Name == nsName)
						return ns.FindByName(name.Substring(posPoint + 1));
				return null;
			}

			foreach (Namespace ns in Namespaces)
			{
				if (ns.Name == name) return ns;
			}
			return null;
		}

		/// <summary>
		/// Searches for namespaces and classes by id
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public ModelElement FindModelElementById(string id)
		{
			foreach (Namespace ns in Namespaces)
			{
				if (ns.Id == id) return ns;

				ModelElement me = ns.FindModelElementById(id);
				if (me != null) return me;
			}

			foreach (Class cls in Classes)
			{
				if (cls.Id == id) return cls;

				foreach (Attribute attr in cls.Attributes)
					if (attr.Id == id) return attr;
				foreach (Operation op in cls.Operations)
					if (op.Id == id) return op;
			}

			return null;
		}

        public ClassList FindClassesByStereotype(string stereotype)
        {
            ClassList list = new ClassList();
            foreach (Class cls in Classes)
            {
                if (cls.StereotypeName != null && cls.StereotypeName.ToLower() == stereotype.ToLower())
                {
                    list.Add(cls);
                }
            }
            foreach (Namespace ns in Namespaces)
            {
                ClassList subList = ns.FindClassesByStereotype(stereotype);
                if (subList.Count > 0)
                    list.AddRange(subList);
            }
            return list;
        }


		/// <summary>
		/// load all classes in that namespace and all classes in all sub namespaces
		/// </summary>
		/// <returns></returns>
		public ClassList DeepGetClasses()
		{
			ClassList results = new ClassList();
			results.AddRange(Classes);
			foreach (Namespace ns in Namespaces)
			{
				results.AddRange(ns.DeepGetClasses());
			}
			return results;
		}

		public System.IO.DirectoryInfo CreateDirectory(System.IO.DirectoryInfo baseDir)
		{
			System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo(baseDir.FullName + "\\" + FullName.Replace('.', '\\'));
			dir.Create();
			return dir;
		}

		public System.IO.DirectoryInfo CreateDirectory(string baseDir)
		{
			return  CreateDirectory(new System.IO.DirectoryInfo(baseDir));
		}


		public ClassList FindSubClassesFor(Class cls)
		{
			ClassList result = new ClassList();
			foreach (Class c in Classes)
			{
				if (c.SuperClassID == cls.Id)
					result.Add(c);
			}

			foreach (Namespace ns in Namespaces)
			{
				result.AddRange(ns.FindSubClassesFor(cls));
			}
			return result;
		}



		public virtual void ValidateRecursive(ValidationIssueList issues)
		{
			this.Validate(issues);
			foreach (Class cls in Classes)
				cls.ValidateRecursive(issues);
			foreach (Namespace ns in Namespaces)
				ns.ValidateRecursive(issues);
		}

		
	}

	#region Helper Types

	public delegate void NamespaceAddDelegate(Namespace ns);

	public class NamespaceList : ArrayList
	{
		public event NamespaceAddDelegate AfterNamespacesAdded;

		public void Add(Namespace ns)
		{
			base.Add(ns);
			if (AfterNamespacesAdded != null)
				AfterNamespacesAdded(ns);
		}

		public Namespace this[int i]
		{
			get
			{
				return base[i] as Namespace;
			}
			set
			{
				base[i] = value;
			}
		}
	}

	#endregion

}
